package cn.seaboot.admin.user.service;

import cn.seaboot.admin.mvc.cache.CacheName;
import cn.seaboot.admin.mvc.Result;
import cn.seaboot.admin.user.bean.Menu;
import cn.seaboot.admin.user.context.UserContext;
import cn.seaboot.admin.user.dao.MenuDao;
import cn.seaboot.commons.core.*;
import cn.seaboot.commons.exception.BizException;
import cn.seaboot.commons.lang.Warning;
import org.jetbrains.annotations.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;

/**
 * dao
 *
 * @author Mr.css
 */
@Service
public class MenuService {

    @Resource
    private MenuDao menuDao;

    @Resource
    private RoleMenuService roleMenuService;

    @Resource
    private UserContext userContext;

    /**
     * 新增1个空菜单，必填项只有menu的Pid，哪怕其它成员变量menu有值
     *
     * @param roleId 角色ID，通常是当前登录角色，新建的菜单权限会直接赋予到该角色
     * @param pid    菜单父级节点
     * @return SysMenu
     */
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = CacheName.MENU, key = "#roleId")
    public Menu insert(String roleId, @NotNull String pid) {
        Menu p = menuDao.selectById(pid);
        String path = this.getNextPathCode(p);

        Menu menu = new Menu();
        menu.setId(path);
        menu.setPid(pid);
        menu.setPath(path);

        menu.setTitle("new node");
        menu.setEditable(Boolean.TRUE);
        menu.setGroupName(p.getGroupName());

        menu.setLevel(p.getLevel() + 1);

        menuDao.insert(menu);
        roleMenuService.insertMenuPerm(roleId, menu.getId());
        return menu;
    }

    /**
     * 菜单编码规则，
     * 例如：根节点为00，第一个子节点则为0001，第二个子节点为0002，
     * 依次类推，每一层最多99个节点。
     *
     * @param menu 父节点
     * @return 路径
     */
    private String getNextPathCode(Menu menu) {
        // 获取最大的子级节点
        String max = menuDao.selectMaxChildrenPath(menu.getId());
        if (CommonUtils.isEmpty(max)) {
            // 如果不存在子级，则定为01编号
            return menu.getPath() + "01";
        } else {
            String code = max.substring(max.length() - 2);
            int currentValue = Integer.parseInt(code);
            if (currentValue >= 99) {
                throw new BizException("菜单栏子级列表已经达到最大容量，不允许继续添加子级菜单!");
            }
            return menu.getPath() + SmartText.fillZero(1 + currentValue, 2);
        }
    }

    /**
     * 删除
     *
     * @param id 菜单ID
     * @return 无异常固定为1
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public int deleteById(String id) {
        Menu menu = menuDao.selectById(id);
        if (menu == null) {
            throw new BizException("菜单信息未找到：" + id);
        }
        //  删除本节点
        menuDao.deleteById(id);
        //  删除子节点
        menuDao.deleteChild(menu.getPath());
        //  清除所有失效的菜单角色配置
        roleMenuService.cleanInvalidRoleMenu();
        return 1;
    }

    /**
     * 更新
     *
     * @param menus pojo
     * @return 受影响行数
     */
    public int updateById(Menu menus) {
        return menuDao.updateSampleById(menus);
    }

    /**
     * 分配权限
     *
     * @param operation 操作描述
     * @param roleId    角色ID，
     * @param ids       菜单ID列表
     * @return 操作结果
     */
    @CacheEvict(cacheNames = CacheName.MENU, key = "#roleId")
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result grantMenus(String roleId, String ids, String operation) {
        // 置空权限
        roleMenuService.deleteRoleMenuPerm(roleId);

        String[] arr = ids.split(",");
        for (String id : arr) {
            roleMenuService.insertMenuPerm(roleId, Converter.toString(id));
        }
        return Result.succeed();
    }

    /**
     * 查询菜单栏，与用户首页相关
     *
     * @param roleId 角色ID
     * @return 权限树
     */
    @Cacheable(cacheNames = CacheName.MENU, key = "#roleId")
    public List<Map<String, Object>> queryMenuAuthority(String groupName, String roleId) {
        List<Map<String, Object>> list = menuDao.selectMenuAuthority(groupName, roleId);

        return new TreeBuilder<Map<String, Object>, Map<String, Object>>()
                .setCompare((p, c) -> MapUtils.getInteger(p, "id").compareTo(MapUtils.getInteger(c, "pid")))
                .setChildSetter((p, c) -> MapUtils.getWithDefault(p, "children", new ArrayList<Map<String, Object>>()).add(c))
                .setParent(list).setChildren(list).groupBy().getParent();
    }

    /**
     * 查找一个角色所拥有的菜单配置
     *
     * @param groupName 菜单组别
     * @param roleId    角色 ID
     * @return 集合
     */
    public List<Menu> selectMenuByRoleId(String groupName, String roleId) {
        return menuDao.selectMenuByRoleId(groupName, roleId);
    }


    /**
     * 查找系统中拥有的所有菜单配置
     *
     * @param groupName 菜单组别
     * @return 集合
     */
    public List<Menu> selectList(String groupName) {
        return menuDao.selectList(groupName);
    }
}
